<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Canvas Text Rendering Character by Character</title>
    <style>
        canvas {
            border: 1px solid #000;
            z-index: 50; /* 确保这个值足够高，以覆盖其他内容 */
            position: absolute; /* 避免占用页面空间 */
        }

         /* 隐藏输入框，但保持其可接受输入 */
         #editableDiv {
        opacity: 0; /* 使div不可见但仍然可以接收输入 */
        position: absolute; /* 避免占用页面空间 */
        top: -100;
        left: -1000;
        width: 110px; /* 最小化可编辑区域的尺寸 */
        height: 10px; /* 最小化可编辑区域的尺寸 */
        overflow: hidden; /* 避免滚动条出现 */
    }

    #contextMenu {
            display: none;
            position: absolute;
            z-index: 1000; /* 确保这个值足够高，以覆盖其他内容 */
            background-color: white;
            border: 1px solid #ddd;
            padding: 10px;
            box-shadow: 3px 3px 5px rgba(0,0,0,0.2); /* 阴影效果 */
        }

        #contextMenu ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        #contextMenu ul li {
            padding: 8px;
            cursor: pointer;
        }

        #contextMenu ul li:hover {
            background-color: #f7f7f7;
        }

        .button-container {
            text-align: center; /* 将按钮居中对齐 */
            margin-top: 10px; /* 在容器和画布之间添加一些空间 */
            position: absolute;
            z-index: 150; /* 确保这个值足够高，以覆盖其他内容 */
        }

    </style>



</head>
<body>
    <canvas id="canvas" width="1240" height="600"></canvas>
     <!-- 看不见的输入框 -->
 
     <div id="editableDiv" contenteditable="true"></div>

     <div id="contextMenu">
        <ul>
            <li id="copy">复制</li>
            <li id="paste">粘贴</li>
        </ul>
    </div>


    <div class="button-container">
        <button id="fontSizeBtn">28</button><button id="fontSizeBtn2">50</button>
        <button id="fontColorBtn">红</button>
        <button id="fontColorBtn2">蓝</button>
        <button id="fontFontBtn">黑体</button>
        <button id="fontFontBtn2">宋体</button>
        <button id="italicBtn">斜体</button><button id="italicBtn2">消斜</button>
        <button id="boldBtn">粗</button><button id="bold2Btn">细</button>
        <button id="underLineBtn">下划</button><button id="underLineBtn2">无下</button>
        <button id="singleLineBtn">直线</button><button id="wavyLineBtn">波浪</button>
        <button id="underLineRedBtn">底线红</button><button id="underLineBlackBtn">底线黑</button>
        <button id="lineSpacing10Btn">10pxLS</button><button id="lineSpacing30Btn">30pxLS</button>
        <button id="firstLineIndentBtn">->0</button><button id="firstLineIndent4Btn">->4</button>
        <button id="horizontalAlign_LeftBtn">-左</button><button id="horizontalAlign_MiddleBtn">-中</button><button id="horizontalAlign_RightBtn">右-</button>
        <button id="verticalAlign_topBtn">顶</button><button id="verticalAlign_MiddleBtn">中</button><button id="verticalAlign_bottomBtn">底</button>
        <button id="verticalAlign_topBtn">顶</button><button id="verticalAlign_MiddleBtn">中</button><button id="verticalAlign_bottomBtn">底</button>
        <button id="bgColorTransparent_Btn">透背</button><button id="bgColorBlue_Btn">蓝背</button><button id="bgColorBlack_Btn">黑背</button>

       

        
       
    </div>


    <script>
           var textData = {
            text: "[checkbox]您好中国！广州力麒智能科技有限公司是高新技术企业、广东省专精特新企业。[checkbox]主营业务涵盖标杆大厅建设咨询输出、国产信创政务产业、软件开发咨询、政务服务能力提升培训及人力服务外包等",
            fontStyles: [
                        { start: 0, end: 2, font: "黑体"},
                        { start: 2, end: 6, font: "楷体"}
                    ],
            fontSize: [
                        { start: 0, end: 12, fontSize:40 },
                        { start: 12, end: 26, fontSize:80}
                    ],           
            underlineColor: [
                        { start: 0, end: 6,color: "red" },
                        { start: 6, end: 16,color: "green" }
                    ], 
            fontColor: [
                        { start: 0, end: 12,color: "red" },
                        { start: 12, end: 26,color: "green" }
                    ],                     
            italic: [ //斜体属性
                { start: 0, end: 4, italic: true }
                    ],
            bold: [ // 加粗
                { start: 3, end: 5, bold: true }
                    ],
            underline: [ // 下划线
                { start: 0, end: 16, underline: true }
                    ],
            underlineStyle:[ // 下划线
                { start: 0, end: 3, style: 'wavy' }
                    ],
            lineThrough: [ // 删除线
                { start: 4, end: 6, lineThrough: true }
                    ],
            background: [ // 背景色
                { start: 5, end: 17, background: 'rgba(255, 255, 0, 0.5)' }
                    ],
            type: [ // 类型
                { start: 5, end: 17, type: 'text' }
                ],
            firstLineIndent: 2,
            lineSpacing: 15, // 行间距
            height: 80 ,     // 假设的行高，实际中应根据最大字体计算
            horizontalAlign: 'left', //水平对齐方式
            verticalAlign: 'top' // 垂直对齐方式
        };

    
       document.addEventListener("DOMContentLoaded", function() {
       


        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
     
        let isSelecting = false;
        let selectionStart = null;
        let selectionEnd = null;
        const charWidths = [];
        let lineHeight = 0;        // 添加行高变量
        let cursorPosition = null; // 光标位置         
        let cursorVisible = true; // 控制光标显示的状态
        let hasSelection = false;  // 添加一个变量来跟踪选择状态
        let isComposing = false;
        let linesData = []; // 用于存储处理后的行数据

        let isDragging = false; // 跟踪是否正在拖拽（即选择文本）
        let dragStartIndex = -1; // 拖拽开始时的字符索引

        const editableDiv = document.getElementById('editableDiv');
        let initialText = "";      // 记录开始输入时的文本
        let initialSelectionStart = 0; // 记录开始输入时的光标或选区开始位置
        let initialSelectionEnd = 0;   // 记录开始输入时的光标或选区结束位置
        let initialTextLength = 0;
		let initialCursorPosition = 0;
        
        editableDiv.style.width = '1px';
        editableDiv.style.height = '1px';
        editableDiv.style.opacity = 0;


        const contextMenu = document.getElementById('contextMenu');
        const copyMenuItem = document.getElementById('copy');
        const pasteMenuItem = document.getElementById('paste');

        const fontSizeBtn = document.getElementById('fontSizeBtn');
        const fontSizeBtn2 = document.getElementById('fontSizeBtn2');
        const fontColorBtn = document.getElementById('fontColorBtn');
        const fontColorBtn2 = document.getElementById('fontColorBtn2');

        const fontFontBtn = document.getElementById('fontFontBtn');
        const fontFontBtn2 = document.getElementById('fontFontBtn2');

        const italicBtn = document.getElementById('italicBtn');
        const italicBtn2 = document.getElementById('italicBtn2');

        const underLineBtn = document.getElementById('underLineBtn');
        const underLineBtn2 = document.getElementById('underLineBtn2');

        const singleLineBtn = document.getElementById('singleLineBtn');
        const wavyLineBtn = document.getElementById('wavyLineBtn');

        const underLineRedBtn = document.getElementById('underLineRedBtn');
        const underLineBlackBtn = document.getElementById('underLineBlackBtn');

        const lineSpacing10Btn = document.getElementById('lineSpacing10Btn');
        const lineSpacing30Btn = document.getElementById('lineSpacing30Btn');
        
        const firstLineIndentBtn = document.getElementById('firstLineIndentBtn');
        const firstLineIndent4Btn = document.getElementById('firstLineIndent4Btn');


       
     
        
        const horizontalAlign_LeftBtn = document.getElementById('horizontalAlign_LeftBtn');
        const horizontalAlign_MiddleBtn = document.getElementById('horizontalAlign_MiddleBtn');
        const horizontalAlign_RightBtn = document.getElementById('horizontalAlign_RightBtn');


        const verticalAlign_topBtn = document.getElementById('verticalAlign_topBtn');
        const verticalAlign_MiddleBtn = document.getElementById('verticalAlign_MiddleBtn');
        const verticalAlign_bottomBtn = document.getElementById('verticalAlign_bottomBtn');
         //boldBtn
        const boldBtn = document.getElementById('boldBtn');
        const bold2Btn = document.getElementById('bold2Btn');


       
        const bgColorTransparent_Btn = document.getElementById('bgColorTransparent_Btn');
        const bgColorBlue_Btn = document.getElementById('bgColorBlue_Btn');
        const bgColorBlack_Btn = document.getElementById('bgColorBlack_Btn');

        fontSizeBtn.addEventListener('click', function() {
            console.log('C3 click TextSizeSet 28');
            updateSelectedTextSize(28);
            compressStyles();
            generateLinesData();
           
            renderText();
        });

        fontSizeBtn2.addEventListener('click', function() {
            console.log('C3 click TextSizeSet 50');
            updateSelectedTextSize(50);
            compressStyles();
              generateLinesData();
           
            renderText();
        });

        fontColorBtn.addEventListener('click', function() {
            updateSelectedTextColor('red');
            compressStyles();
            renderText();
        });

        fontColorBtn2.addEventListener('click', function() {
            updateSelectedTextColor('blue');
            compressStyles();
            renderText();
        });

        fontFontBtn.addEventListener('click', function() {
            updateSelectedFont("黑体");
            compressStyles();
            renderText();
        });

        italicBtn.addEventListener('click', function() {
            updateSelectedItalic(true);
            compressStyles();
            renderText();
        });

        italicBtn2.addEventListener('click', function() {
            updateSelectedItalic(false);
            compressStyles();
            renderText();
        });

        
        underLineBtn.addEventListener('click', function() {
            updateSelectedUnderLine(true);
            compressStyles();
            renderText();
        });

        underLineBtn2.addEventListener('click', function() {
            updateSelectedUnderLine(false);
            compressStyles();
            renderText();
        });


         
        singleLineBtn.addEventListener('click', function() {
            updateSelectedUnderLine(true);
            compressStyles();
            updateSelectedUnderLineStyle("single");
            compressStyles();
            generateLinesData();
           
      
            renderText();
        });

         
        wavyLineBtn.addEventListener('click', function() {
            updateSelectedUnderLine(true);
            compressStyles();
            updateSelectedUnderLineStyle("wavy");
            compressStyles();
            generateLinesData();
           
            renderText();
        });
     // underLineBlueBtn underLineBlackBtn  underlineColor
     underLineRedBtn.addEventListener('click', function() {
        updateSelectedUnderLine(true);
        compressStyles();  
        updateSelectedUnderLineColor("red");            
        compressStyles();           
        generateLinesData();           
        renderText();
        });

        underLineBlackBtn.addEventListener('click', function() {
            updateSelectedUnderLineColor("black");
            updateSelectedUnderLine(true);
            compressStyles();
            generateLinesData();           
            renderText();
        });

        
        lineSpacing10Btn.addEventListener('click', function() {
          
            updateSelectedLineSpacing(10);
           
            generateLinesData();           
            renderText();
        });

        lineSpacing30Btn.addEventListener('click', function() {
            updateSelectedLineSpacing(30);
           
            generateLinesData();           
            renderText();
        });

        firstLineIndentBtn.addEventListener('click', function() {
            updateSelectedFirstLineIndent(0);
                
            generateLinesData();           
            renderText();
        });

        firstLineIndent4Btn.addEventListener('click', function() {
            updateSelectedFirstLineIndent(4);
                
            generateLinesData();           
            renderText();
        });

        horizontalAlign_LeftBtn.addEventListener('click', function() {
            updateHorizontalAlign("left");
                
            generateLinesData();           
            renderText();
        });

        horizontalAlign_MiddleBtn.addEventListener('click', function() {
            updateHorizontalAlign("center");
                
            generateLinesData();           
            renderText();
        });

        horizontalAlign_RightBtn.addEventListener('click', function() {
            updateHorizontalAlign("right");
                
            generateLinesData();           
            renderText();
        });

        verticalAlign_topBtn.addEventListener('click', function() {
            updateVerticalAlign("top");
                
            generateLinesData();           
            renderText();
        });

        verticalAlign_MiddleBtn.addEventListener('click', function() {
            updateVerticalAlign("middle");
                
            generateLinesData();           
            renderText();
        });

        verticalAlign_bottomBtn.addEventListener('click', function() {
            updateVerticalAlign("bottom");
                
            generateLinesData();           
            renderText();
        });
          
        

        boldBtn.addEventListener('click', function() {
            updateSelectedBold(true);
            compressStyles();
             
            generateLinesData();           
            renderText();
        });
          

        bold2Btn.addEventListener('click', function() {
            updateSelectedBold(false);
            compressStyles();                
            generateLinesData();           
            renderText();
        });
          
           // bgColorTransparent_Btn  bgColorBlue_Btn  bgColorBlack_Btn
          

     
           bgColorTransparent_Btn.addEventListener('click', function() {
            updateSelectedGBColor("transparent");
            compressStyles();                
            generateLinesData();           
            renderText();
        });

        bgColorBlue_Btn.addEventListener('click', function() {
            updateSelectedGBColor('rgba(20, 255, 80, 0.5)');
            compressStyles();                
            generateLinesData();           
            renderText();
        });

        bgColorBlack_Btn.addEventListener('click', function() {
            updateSelectedGBColor('rgba(0, 0, 0, 0.5)');
            compressStyles();                
            generateLinesData();           
            renderText();
        });

function updateSelectedTextSize(newFontSize) {
    // 更新fontSize数组
    updateStyleProperty(textData.fontSize, newFontSize, "fontSize");

    // 根据textData重新计算linesData中的样式，这一步骤取决于你的linesData构建方式
    // 通常，你需要根据textData中的全局样式来更新每一行的局部样式
    updateLinesDataStyles();

    // 重新渲染文本以应用字体大小的更新
    renderText();
}




    function updateLinesDataStyles() {
            // 这个函数需要根据你的具体实现来编写
            // 基本思路是遍历linesData的每一行，根据textData的styles来更新行内的styles
            generateLinesData();
           // 重新生成linesData，包括文本、样式和行高
    		

            // 接下来，你可能需要根据新的linesData来更新文档的其他相关属性，
            // 比如整体高度等，这取决于你的具体实现。
            updateTextLayout(); // 假设这个函数处理文档布局的其他更新
    }

        function updateTextLayout() {
            let currentY = 0; // 假设文档的起始Y坐标为0
            linesData.forEach(line => {
                // 假设line对象有一个y属性，表示该行的起始Y坐标
                line.y = currentY;
                currentY += line.height; // 更新下一行的起始Y坐标
            });

            // 更新整个文档的高度
            textData.totalHeight = currentY;
        }

function updateSelectedTextColor(newColor) {
    updateStyleProperty(textData.fontColor, newColor, 'color');
}
function updateStyleProperty(styleArray, newValue, property) {
    let newStyles = [];
    let hasOverlap = false; // 标记选区内的样式是否与现有样式有重叠

    // 检查并更新现有样式
    styleArray.forEach(style => {
        if (style.end <= selectionStart || style.start >= selectionEnd) {
            // 完全不在选区内的样式直接保留
            newStyles.push(style);
        } else {
            hasOverlap = true; // 选区内至少有一部分样式与现有样式重叠

            // 部分或完全在选区内的样式需要细分和更新
            if (style.start < selectionStart) {
                // 选区前的样式分割出来
                newStyles.push({
                    ...style,
                    end: selectionStart,
                });
            }

            // 更新选区内的样式
            newStyles.push({
                start: Math.max(style.start, selectionStart),
                end: Math.min(style.end, selectionEnd),
                [property]: newValue,
            });

            if (style.end > selectionEnd) {
                // 选区后的样式分割出来
                newStyles.push({
                    ...style,
                    start: selectionEnd,
                });
            }
        }
    });

    // 如果选区内原本没有完全匹配的样式，则为选区创建新的样式段
    if (!hasOverlap) {
        newStyles.push({start: selectionStart, end: selectionEnd, [property]: newValue});
    } else {
        // 确保覆盖整个选区
        ensureFullCoverage(newStyles, selectionStart, selectionEnd, newValue, property);
    }

    // 用新的样式数组替换旧的
    styleArray.splice(0, styleArray.length, ...newStyles);
}

function ensureFullCoverage(styles, start, end, value, property) {
    // 查找选区起点和终点之间是否存在未覆盖的区域
    let covered = new Array(end - start).fill(false);

    styles.forEach(style => {
        if (style[property] === value) {
            for (let i = Math.max(style.start, start); i < Math.min(style.end, end); i++) {
                covered[i - start] = true;
            }
        }
    });

    // 填充未覆盖的区域
    for (let i = 0; i < covered.length; i++) {
        if (!covered[i]) { // 发现未覆盖区域
            let newStart = i + start;
            let newEnd = newStart + 1;

            // 扩展这个未覆盖区域到其最大长度
            while (newEnd < end && !covered[newEnd - start]) {
                newEnd++;
            }

            // 添加新的样式段以覆盖这个区域
            styles.push({start: newStart, end: newEnd, [property]: value});

            // 更新覆盖数组
            for (let j = newStart; j < newEnd; j++) {
                covered[j - start] = true;
            }

            i = newEnd - start - 1; // 跳过已处理区域
        }
    }
}

        function mergeStyles(styles) {
            // 此函数用于合并同一行中相邻且样式相同的样式块
            // 注意：这里的实现取决于具体需求，可能需要根据实际情况调整
            return styles.sort((a, b) => a.start - b.start); // 简单示例：按开始位置排序
        }

        function findLineIndexForPosition(position) {
            // 返回给定位置的行索引
            for (let i = 0; i < linesData.length; i++) {
                if (linesData[i].start <= position && position <= linesData[i].end) {
                    return i;
                }
            }
            return -1; // 如果位置不在任何行中，则返回-1
        }


function updateSelectedFont(newFont) {
    updateStyleProperty(textData.fontStyles, newFont, 'font');
}

function updateSelectedItalic(isItalic) {
    updateStyleProperty(textData.italic, isItalic, "italic");
}

function updateSelectedUnderLine(hasUnderline) {
    updateStyleProperty(textData.underline, hasUnderline, "underline");
}


function updateSelectedUnderLineStyle(underLineStyle) {
    updateStyleProperty(textData.underlineStyle, underLineStyle, "style");
}


function updateSelectedUnderLineColor(underlineColor) {
    console.log("C4 underlineColor: " + underlineColor," textData.underlineColor: " + textData.underlineColor);
  
    updateStyleProperty(textData.underlineColor, underlineColor, "color");
    
}

function updateSelectedLineSpacing(lineSpacing) {
    textData.lineSpacing=lineSpacing;
}



function updateSelectedFirstLineIndent(Indent) {
    textData.firstLineIndent=Indent;
}


function updateHorizontalAlign(alignment) {
    textData.horizontalAlign=alignment;
}

function updateVerticalAlign(alignment) {
    textData.verticalAlign=alignment;
}

function updateSelectedBold(isBold) {
    updateStyleProperty(textData.bold, isBold, "bold");
}

function updateSelectedGBColor(color) {
    updateStyleProperty(textData.background, color, "background");
}

function compressStyles() {
    compressEachStyleProperty(textData.fontStyles, 'font');
    compressEachStyleProperty(textData.fontSize, 'fontSize');
    compressEachStyleProperty(textData.fontColor, 'color');
    compressEachStyleProperty(textData.italic, 'italic');
    compressEachStyleProperty(textData.bold, 'bold');
    compressEachStyleProperty(textData.underline, 'underline');
    compressEachStyleProperty(textData.underlineStyle, 'style');
    compressEachStyleProperty(textData.underlineColor, 'underlineColor');
    compressEachStyleProperty(textData.background, 'background');
    
}

    function compressEachStyleProperty(styleArray, property) {
        if (styleArray.length === 0) return;

        // 首先，根据样式段的开始位置进行排序，确保样式按顺序处理
        styleArray.sort((a, b) => a.start - b.start);

        let compressedStyles = [styleArray[0]];

        for (let i = 1; i < styleArray.length; i++) {
            let lastStyle = compressedStyles[compressedStyles.length - 1];
            let currentStyle = styleArray[i];

            // 检查当前样式是否与上一个样式相同，并且连续
            if (lastStyle[property] === currentStyle[property] && lastStyle.end === currentStyle.start) {
                // 如果当前样式与上一个样式在属性上相同且连续，则合并这两个样式段
                lastStyle.end = currentStyle.end;
            } else {
                compressedStyles.push(currentStyle);
            }
        }

        // 更新原样式数组为压缩后的样式数组
        styleArray.splice(0, styleArray.length, ...compressedStyles);
    }



    canvas.addEventListener('contextmenu', (e) => {
            e.preventDefault(); // 阻止默认的右键菜单

            const rect = canvas.getBoundingClientRect(); // 获取canvas的边界
            const menuWidth = contextMenu.offsetWidth;
            const menuHeight = contextMenu.offsetHeight;

            // 计算菜单的最佳位置，确保它不会超出canvas边界
            let x = e.clientX - rect.left; // 转换为相对于canvas的位置
            let y = e.clientY - rect.top;

            console.log(x, y);
            // 如果菜单超出右侧
            if (x + menuWidth > canvas.width) {
                x = canvas.width - menuWidth;
            }
            // 如果菜单超出底部
            if (y + menuHeight > canvas.height) {
                y = canvas.height - menuHeight;
            }

            // 设置自定义菜单的位置并显示
            contextMenu.style.top = `${y}px`;
            contextMenu.style.left = `${x}px`;
            contextMenu.style.display = 'block';
        });


        // 点击复制
        copyMenuItem.addEventListener('click', () => {
            copyToClipboard();  // 前面定义的复制函数
            contextMenu.style.display = 'none';  // 隐藏菜单
        });

        // 点击粘贴
        pasteMenuItem.addEventListener('click', () => {
            // 粘贴内容到画布并更新textData.text
            navigator.clipboard.readText().then((text) => {
                // 假设我们粘贴到光标当前位置
                insertTextAtCursor(text);
                renderText();  // 重新渲染画布
            });
            contextMenu.style.display = 'none';  // 隐藏菜单
        });

        // 点击其他地方时隐藏菜单
        document.addEventListener('click', (e) => {
            if (e.target !== contextMenu) {
                contextMenu.style.display = 'none';
            }
        });
   

    function insertTextAtCursor(text) {
        // 粘贴时的文本插入逻辑
        const beforeCursor = textData.text.slice(0, cursorPosition);
        const afterCursor = textData.text.slice(cursorPosition);
        textData.text = beforeCursor + text + afterCursor;
        cursorPosition += text.length;  // 更新光标位置
        // 需要更新样式信息（省略）
    }



    canvas.addEventListener('click', function() {
        editableDiv.focus(); // 当画布被点击时，设置焦点到editableDiv
    	});

        // 根据需要添加更多事件监听器，如compositionstart, compositionupdate

        // 开始输入复合字符（如中文字符）
        editableDiv.addEventListener('compositionstart', function(e) {
            isComposing = true;
            console.log('开始输入  =================================');
            initialText = textData.text;
            initialSelectionStart = selectionStart; // 假设你已经有这些值
            initialSelectionEnd = selectionEnd;
                        
        
        });

        // 输入复合字符过程中（可选，如果你需要实时获取输入法中的文本，可以使用此事件）
    editableDiv.addEventListener('compositionupdate', function(e) {
            // e.data 包含当前输入法组合中的文本
            console.log('输入更新  	:', e.data);
            clearSelection();
        });


    editableDiv.addEventListener('compositionend', function(e) {
            console.log(e.data); // 打印输入的文字
            // 更新canvas的文本内容
            isComposing = false;
                // // e.data 包含完成输入的复合字符
                // // 在这里插入字符到文本中
                const beforeCursor = textData.text.slice(0, cursorPosition);
                const afterCursor = textData.text.slice(cursorPosition);
                textData.text = beforeCursor + e.data + afterCursor;
                updateStylesForInsert(cursorPosition,e.data.length);
                cursorPosition += e.data.length; // 更新光标位置，这里假设插入文本的长度就是e.data的长度
                console.log('C1 输入完成	:', e.data," 字符串长",e.data.length);

               
                electionStart = null;
                selectionEnd = null;
            
                cursorVisible = true; // 可选：同时隐藏光标             

                renderText(); // 重新渲染文本和样式
        });

        // 确保editableDiv可以接收输入
       
    editableDiv.addEventListener('paste', function(e) {
            e.preventDefault(); // 阻止默认粘贴行为

            // 获取剪贴板中的文本内容
            const pasteText = e.clipboardData.getData('text');

            // 假定cursorPosition是当前光标在textData.text中的位置
            if (cursorPosition === null) {
                cursorPosition = textData.text.length; // 如果没有指定光标位置，则默认为文本末尾
            }

            // 将文本插入到光标位置
            const beforeCursor = textData.text.slice(0, cursorPosition);
            const afterCursor = textData.text.slice(cursorPosition);
            textData.text = beforeCursor + pasteText + afterCursor;

          

            // 更新样式（这个示例没有改变样式，但你可能需要根据插入的文本更新样式）
            updateStylesForInsert(cursorPosition, pasteText.length);

              // 更新光标位置
              cursorPosition += pasteText.length;

            // 重新渲染文本
            renderText();
        });

    function copyToClipboard() {

        if (selectionStart === null || selectionEnd === null || selectionStart === selectionEnd) {
            console.log("没有选中的文本可以复制");
            return;
        }

        // 提取选中的文本
        const selectedText = textData.text.slice(selectionStart, selectionEnd);

        // 创建一个临时的textarea元素用于复制操作
        const textarea = document.createElement('textarea');
        textarea.value = selectedText;
        document.body.appendChild(textarea);

        // 选中文本并复制
        textarea.select();
        document.execCommand('copy');

        // 移除临时创建的textarea
        document.body.removeChild(textarea);

        console.log("已复制到剪贴板: " + selectedText);
    }



   

    function insertTextAtPosition(text, position) {
        // 插入文本并更新textData.text
        const beforeText = textData.text.substring(0, position);
        const afterText = textData.text.substring(position);
        textData.text = beforeText + text + afterText;

        // 更新样式范围
        updateStylesForInsert(position, text.length);

        // 重新渲染文本（这取决于你的渲染逻辑）
        renderText();
    }

        // 调整原有的keydown事件监听器，只在非组合输入时处理
        document.addEventListener('keydown', function(e) {
            if (!isComposing && e.key.length === 1) {
                // 处理英文输入等非复合字符输入
            }
            // 其他按键处理，比如Backspace, Delete等
        });


        function isInSelection(x, y) {
    // 首先，确定y坐标所在的行
    let accumulatedHeight = 50; // 初始行高，根据实际情况调整
    let globalCharIndex = 0; // 全局字符索引

    for (let line of linesData) {
        if (y >= accumulatedHeight && y < accumulatedHeight + line.height) {
            // 现在，我们找到了y坐标所在的行，接下来确定x坐标是否在选区内
            if (globalCharIndex + line.text.length < selectionStart || globalCharIndex > selectionEnd) {
                // 如果整行都在选区之外
                return false;
            }

            // 计算行内的选区开始和结束位置
            const lineSelectionStart = Math.max(selectionStart - globalCharIndex, 0);
            const lineSelectionEnd = Math.min(selectionEnd - globalCharIndex, line.text.length);

            // 累加当前行字符的宽度，以确定选区的x坐标范围
            let cursorX = 0;
            for (let i = 0; i < line.text.length; i++) {
                if (i >= lineSelectionStart && i < lineSelectionEnd) {
                    // 如果在选区范围内，检查x坐标是否落在当前字符的宽度范围内
                    const char = line.text[i];
                    const charStyle = line.styles.find(s => i >= s.start && i < s.end) || { fontSize: 16, font: "Arial", color: "black" };
                    ctx.font = `${charStyle.fontSize}px ${charStyle.font}`;
                    const metrics = ctx.measureText(char);
                    const charWidth = metrics.width;

                    if (x >= cursorX && x < cursorX + charWidth) {
                        // 如果x坐标在当前字符的宽度范围内，则说明光标在选区内
                        return true;
                    }
                }
                // 更新cursorX以反映当前行内的位置
                cursorX += ctx.measureText(line.text[i]).width;
            }

            // 如果循环结束还没有找到匹配，说明点击位置不在选区内
            return false;
        }
        accumulatedHeight += line.height; // 更新累加的行高
        globalCharIndex += line.text.length; // 更新全局字符索引
    }

    // 如果循环结束还没有找到匹配的行，说明点击位置不在任何选区内
    return false;
}
	let placeholderIdCounter = 0;

    function generatePlaceholderId() {
        return placeholderIdCounter++; // 简单的递增ID生成器
    }

    function getPlaceholderWidth(placeholder, fontSize) {
        // 假设占位符的宽度与字体大小一致
        return fontSize;
    }

	let hadGen = 0;
    function generateLinesData() {
        linesData = []; // 重置行数据
        let currentLineText = '';
        let currentLineStyles = [];
        let cursorX = 0;
        let currentLineMaxHeight = 0; // 当前行的最大字体高度
        let currentLineStartIndex = 0; // 记录当前行在原文中的起始位置
        let firstSpacing = true;//首行缩进所需的空格
        let indentWidth = 0; // 首行缩进的宽度
        let currentLineWidth=0;
        let isFirstLine=true;
        
        

         // 计算首行缩进的宽度
        if (textData.firstLineIndent>0 && textData.text.length > 0) {
            // 找到首字符的样式
            const firstCharFontStyle = textData.fontStyles.find(s => 0 >= s.start && 0 < s.end) || { font: "Arial" };
            const firstCharFontSize = textData.fontSize.find(s => 0 >= s.start && 0 < s.end) || { fontSize: 16 };

            ctx.font = `${firstCharFontSize.fontSize}px ${firstCharFontStyle.font}`;
            // 使用首字符的字体和大小计算一个字符的宽度，然后乘以缩进字符数
            indentWidth = ctx.measureText(" ").width * textData.firstLineIndent;
            cursorX= indentWidth; // 调整首行的起始cursorX值
            currentLineWidth = indentWidth; // Include indent in the line width

            
        }
       

        console.log("C4 ====generateLinesData cursorX: ",cursorX);
        const text = textData.text;
        let index=0;

        for (index = 0; index < text.length;) { 
        const char = text[index];       
        let isPlaceholder = false;
        let nextCharX = cursorX;
            // 查找字符对应的样式，没有则使用默认样式
             
            const charFont = textData.fontStyles.find(s => index >= s.start && index < s.end) || { font: "Arial" };
            const charFontSize = textData.fontSize.find(s => index >= s.start && index < s.end) || { fontSize: 16 };
            const charColor = textData.fontColor.find(s => index >= s.start && index < s.end) || { color: "black" };
            const charUnderLine = textData.underline.find(s => index >= s.start && index < s.end) || { underline: "true"};
            const charUnderlineStyle=textData.underlineStyle.find(s => index >= s.start && index < s.end) || { style: "wavy"};
            const charUnderlineColor=textData.underlineColor.find(s => index >= s.start && index < s.end) || { color: "red"};
            const charGBColor=textData.background.find(s => index >= s.start && index < s.end) || { background: "blue"};
            const charType=textData.type.find(s => index >= s.start && index < s.end) || { type: "text"};
            const charStyle = {
                type: charType.type,
                font: charFont.font,
                fontSize: charFontSize.fontSize,
                color: charColor.color,
                underline: charUnderLine.underline,
                underlineStyle: charUnderlineStyle.style,
                underlineColor: charUnderlineColor.color,  
                lineWidth: currentLineWidth,
                maxHeight: currentLineMaxHeight,                
                background: charGBColor.color,
                start: index - currentLineStartIndex, // 相对于当前行的开始位置
                end: index - currentLineStartIndex + 1 // 相对于当前行的结束位置
            };

            ctx.font = `${charStyle.fontSize}px ${charStyle.font}`;
            const metrics = ctx.measureText(char);
            var charWidth = metrics.width;

            //console.log("C4 myChar:",char);
            // 更新当前行中的最大字体高度
            currentLineMaxHeight = Math.max(currentLineMaxHeight, charStyle.fontSize);
            //每到一个位置，试探有无占位符，如果有标记一下，完整跳过这个占位符。
            if (text.substring(index).startsWith("[checkbox]")) {
                    const placeholderWidth = getPlaceholderWidth("[checkbox]", findCharStyle(index).fontSize);
                    charWidth = placeholderWidth;
                    nextCharX += placeholderWidth;
                } else {
                    const charMetrics = ctx.measureText(char);
                    charWidth = charMetrics.width;
                    nextCharX += charWidth;
                }

                // 检查是否需要换行
                if (nextCharX > canvas.width && currentLineText) {
                    pushLineData(); // 推送当前行到linesData
                    resetLine(); // 重置行数据
                    continue; // 继续处理当前字符
                }

                // 累积字符到当前行
                accumulateChar(char, index, charStyle, charWidth);
                index++; // 移到下一个字符
           

             // 检查是否需要换行
            if (cursorX + charWidth > canvas.width && currentLineText) {
                pushLineData();
                resetLine();
            } 
        
            
          
        };// 添加最后一行，如果存在
            if (currentLineText) {
                pushLineData();
            }

    function accumulateChar(char, index, charStyle, charWidth) {
        currentLineText += char; // 累积文本
        cursorX += charWidth; // 更新cursorX
        currentLineWidth += charWidth; // 更新当前行宽
        currentLineMaxHeight = Math.max(currentLineMaxHeight, charStyle.fontSize); // 更新最大高度

        // 处理普通字符和占位符的样式累积
        if (text.substring(index).startsWith("[checkbox]")) {
            const placeholder = "[checkbox]";
            const placeholderWidth = getPlaceholderWidth(placeholder, charStyle.fontSize);

            currentLineStyles.push({
                ...charStyle, // 复制原有样式属性
                type: 'placeholder', // 添加type属性
                placeholder: placeholder,
                placeholderWidth: placeholderWidth,
                start: index - currentLineStartIndex,
                end: index - currentLineStartIndex + placeholder.length
            });
            //console.log("accessibility:placeholder",index);
        } else {
            currentLineStyles.push({
                ...charStyle, // 复制原有样式属性
                type: 'text', // 明确标记为普通文本
                width: charWidth,
                start: index - currentLineStartIndex,
                end: index - currentLineStartIndex + 1
            });
            //console.log("accessibility:text",index);
        }
    }


        function pushLineData() {
        // 推送当前行到linesData
            linesData.push({
                text: currentLineText,
                styles: JSON.parse(JSON.stringify(currentLineStyles)),
                y: 0,
                lineWidth: currentLineWidth,
                maxHeight: currentLineMaxHeight,
                startIndex: currentLineStartIndex
            });
        }


        function resetLine() {
            // 重置行数据
            currentLineText = '';
            currentLineStyles = [];
            cursorX = 0;
            currentLineMaxHeight = 0;
            currentLineLineWidth = 0;
            currentLineStartIndex = index; // 更新为新行的起始索引
        }
        // 现在更新每行的y坐标
        updateLineYCoordinates();
    }

    function updateLineYCoordinates() {
        let currentY = 50; // 假设文档的起始Y坐标为50
        linesData.forEach(line => {
            console.log("insert lineInto:", currentY," line.maxHeight=" + line.height," lineSpacing:" + textData.lineSpacing);
            line.y = currentY; // 设置当前行的Y坐标
            currentY += line.maxHeight + textData.lineSpacing; // 假设行间距为15，更新下一行的起始Y坐标
        });
    }


    function updateCurrentLineStyles(currentLineStyles, charStyle, relativeIndex) {
        let styleExists = currentLineStyles.some(style => {
            return style.font === charStyle.font &&
                style.fontSize === charStyle.fontSize &&
                style.color === charStyle.color &&
                style.underline === charStyle.underline &&
                style.underlineStyle === charStyle.style &&
                style.underlineColor === charStyle.color &&
                style.end === relativeIndex
        });

        if (!styleExists) {
            currentLineStyles.push({ ...charStyle, type:"text",start: relativeIndex, end: relativeIndex + 1 });
        } else {
            currentLineStyles[currentLineStyles.length - 1].end = relativeIndex + 1;
        }
    }



      



    function renderText() {
            if (hadGen == 0) {
                generateLinesData();
               console.log("C4 renderText called   ");
                hadGen = 1; // 确保generateLinesData只被调用一次，除非有更新
            }

    		ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布，准备新的渲染
            // 计算整体文本高度
            const totalTextHeight = linesData[linesData.length - 1].y + linesData[linesData.length - 1].maxHeight;
            
            // 根据垂直对齐方式计算起始Y坐标偏移
            let startYOffset = 0;
            if (textData.verticalAlign === 'middle') {
                startYOffset = (canvas.height - totalTextHeight) / 2;
            } else if (textData.verticalAlign === 'bottom') {
                startYOffset = canvas.height - totalTextHeight;
            }
            // 对于顶部对齐，startYOffset保持为0

            linesData.forEach((line, lineIndex) => {
                //let cursorX = 0; // 每行的起始X坐标
                let maxAscent = 0;
                let maxDescent = 0;
                let wavyLineStart = null; // 用于记录波浪线起始位置
                let wavyLineColor = "black"; // 默认波浪线颜色
                let cursorX = line.cursorX!==undefined?line.cursorX:0; // 使用每行特定的cursorX开始渲染
               
                //console.log("C4 cursorX:",cursorX);

                
                if (textData.horizontalAlign === 'center') {
                    console.log("C4 horizontalAlign center line.lineWidth: ",line.lineWidth);
                    cursorX = (canvas.width - line.lineWidth) / 2;
                } else if (textData.horizontalAlign === 'right') {
                    cursorX = canvas.width - line.lineWidth;
                    console.log("C4 horizontalAlign center line.right: ",line.lineWidth);
                }

           
				console.log("C4 cursorX:",cursorX);
                // 首先，遍历一次以找出每行最大的ascent和descent
                for (let i = 0; i < line.text.length; i++) {
                    const charIndex = i + line.startIndex;
                    const charStyle = findCharStyle(charIndex);
                    ctx.font = `${charStyle.bold ? 'bold ' : ''}${charStyle.italic ? 'italic ' : ''}${charStyle.fontSize}px ${charStyle.font}`;
                    const metrics = ctx.measureText(line.text[i]);
                    maxAscent = Math.max(maxAscent, metrics.actualBoundingBoxAscent);
                    maxDescent = Math.max(maxDescent, metrics.actualBoundingBoxDescent);
                }

            //let baseLineY = line.y + maxAscent; // 使用最大ascent来调整基线，确保垂直对齐
            console.log("### line.y:",line.y," startYoffset:",startYOffset+" maxAscent:",maxAscent);
            const baseLineY = line.y + startYOffset+ maxAscent; // 调整y坐标，基于垂直对齐偏移


           


        // 然后，实际渲染每个字符
        for (let i = 0; i < line.text.length; i++) {
            const char = line.text[i];
            var charIndex = i + line.startIndex;
            const charStyle = findCharStyle(charIndex);
          
            var underlineColor = charStyle.underlineColor || "black"; 

            ctx.font = `${charStyle.bold ? 'bold ' : ''}${charStyle.italic ? 'italic ' : ''}${charStyle.fontSize}px ${charStyle.font}`;
            ctx.fillStyle = charStyle.color;
            const metrics = ctx.measureText(char);

            // 在绘制字符之前绘制背景色，如果定义了背景色
            if (charStyle.background) {
                ctx.fillStyle = charStyle.background;
                const metrics = ctx.measureText(char);
                // 考虑行高(maxAscent + maxDescent)来填充背景色
                ctx.fillRect(cursorX, baseLineY - maxAscent, metrics.width, maxAscent + maxDescent);
            }

            // 如果字符处于选中状态，绘制高亮背景
                if (isCharSelected(charIndex)) {
                    const metrics = ctx.measureText(char);
                    ctx.fillStyle = 'rgba(173, 216, 230, 0.5)'; // 高亮颜色，可自定义
                    ctx.fillRect(cursorX, baseLineY - maxAscent, metrics.width, maxAscent + maxDescent);
                    ctx.fillStyle = charStyle.color; // 重置为字符颜色以便绘制文本
                }

              
            //line.styles.forEach((style) => 
           // {
                //console.log("------------------------",style.type);
                let typeOfPlaceholder ="";
               if(line.styles[charIndex] && line.styles[charIndex].type)
               {
                 typeOfPlaceholder = (typeof(line.styles[charIndex].type)!=null)?line.styles[charIndex].type :"text";
               }
                

            if (typeOfPlaceholder === 'placeholder') {
                // 检测到占位符，根据类型绘制相应的图形
               // switch (style.placeholder) {
                //    case '[checkbox]':
                        drawCheckbox(cursorX, baseLineY - line.styles[charIndex].fontSize, line.styles[charIndex].fontSize, line.styles[charIndex].id);
                        //console.log("pring-----style:",style.type);
                //        break;
                    // 可以添加更多的占位符类型处理
               // }
                cursorX += line.styles[charIndex].placeholderWidth; // 移动光标，为下一个字符或占位符留出空间
                i+=11;
                charIndex+=11;
                }
                else
                {

                // 继续使用字符的前景色绘制字符
                ctx.font = `${charStyle.bold ? 'bold ' : ''}${charStyle.italic ? 'italic ' : ''}${line.styles[charIndex].fontSize}px ${charStyle.font}`;
                ctx.fillStyle = charStyle.color;
                const metrics = ctx.measureText(char);
                ctx.fillStyle =line.styles[charIndex].color;
                ctx.fillText(char, cursorX, baseLineY); // 绘制字符
                console.log("pring-----style cursorX:",cursorX," cursorY:",baseLineY," char:",char, " charStyle.color:",line.styles[charIndex].color," line.styles[charIndex].fontSize:",line.styles[charIndex].fontSize);
                var underlineY = baseLineY + 5; // 基线下方的Y坐标
                // 处理下划线
                
                if (charStyle.underline) {
                    underlineY = baseLineY + 5; // 基线下方的Y坐标
                    
                    if (charStyle.underlineStyle === 'wavy') {
                        if (wavyLineStart === null) {
                            wavyLineStart = cursorX;
                            wavyLineColor = underlineColor; // 记录波浪线颜色

                        // console.log("C4 wavy line color: " + wavyLineColor);
                        }

                        
                    } else {
                        if (wavyLineStart !== null) {
                            drawWavyUnderline(wavyLineStart, underlineY, cursorX - wavyLineStart, wavyLineColor);
                            wavyLineStart = null;
                    
                        }
                        drawUnderline(cursorX, underlineY, metrics.width, underlineColor, charStyle.underlineColor);
                    }
                } else if (wavyLineStart !== null) {
                    drawWavyUnderline(wavyLineStart, underlineY, cursorX - wavyLineStart, wavyLineColor);
                    wavyLineStart = null;
                }

                cursorX += metrics.width;

                
                }
            
           // });
           // 行末处理，确保波浪线被绘制
            if (wavyLineStart !== null) {
                const underlineY = baseLineY + 5; // 基线下方的Y坐标
                drawWavyUnderline(wavyLineStart, underlineY, cursorX - wavyLineStart, underlineColor);
            }
            
          
        }

        
    });
}
function drawCheckbox(x, y, size, id) {
    // 简单的复选框绘制逻辑，可以根据需要添加更复杂的逻辑
    ctx.beginPath();
    ctx.rect(x, y, size, size);
    ctx.fillStyle = 'white';
    ctx.fill();
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'black';
    ctx.stroke();
    ctx.closePath();

    // 可以在这里添加勾选状态的绘制逻辑，可能需要额外的信息来决定是否勾选
}
function drawUnderline(x, y, width, color, style) {
    ctx.beginPath();
    ctx.strokeStyle = color;
    ctx.moveTo(x, y);
    ctx.lineTo(x + width, y);
    ctx.stroke();

    if (style === 'double') {
        ctx.moveTo(x, y + 2);
        ctx.lineTo(x + width, y + 2);
        ctx.stroke();
    }
}


function drawWavyUnderline(x, y, width, color) {
    ctx.beginPath();
    ctx.strokeStyle = color;
    let amplitude = 3;
    let frequency = 5;
    for (let i = x; i <= x + width; i += frequency) {
        ctx.quadraticCurveTo(
            i + frequency / 4,
            y + ((i % (frequency * 2)) < frequency ? amplitude : -amplitude),
            i + frequency / 2,
            y
        );
    }
    ctx.stroke();
}


    function isCharSelected(index) {
        // 假设有全局变量selectionStart和selectionEnd定义了选区的开始和结束
        return index >= selectionStart && index < selectionEnd;
    }


    function findCharStyle(index) {
        const charFont = textData.fontStyles.find(s => index >= s.start && index < s.end) || { font: "Arial" };
        const charFontSize = textData.fontSize.find(s => index >= s.start && index < s.end) || { fontSize: 16 };
        const charColor = textData.fontColor.find(s => index >= s.start && index < s.end) || { color: "black" };
        const charItalic = textData.italic.find(s => index >= s.start && index < s.end) || { italic: false };
        const charBold = textData.bold.find(s => index >= s.start && index < s.end) || { bold: false };
        const charUnderline = textData.underline.find(s => index >= s.start && index < s.end) || { underline: false };
        const charLineThrough = textData.lineThrough.find(s => index >= s.start && index < s.end) || { lineThrough: false };
        const charBackground = textData.background.find(s => index >= s.start && index < s.end) || { background: "transparent" };
        const charUnderlineStyle=textData.underlineStyle.find(s => index >= s.start && index < s.end) || {style: 'wavy'};
        const charUnderlineColor = textData.underlineColor.find(s => index >= s.start && index < s.end) || { color: "red" };
        const charType = textData.underlineColor.find(s => index >= s.start && index < s.end) || { type: "text" };
        

        return {
            font: charFont.font,
            type: charType.type,
            fontSize: charFontSize.fontSize,
            color: charColor.color,
            italic: charItalic.italic,
            bold: charBold.bold,
            underline: charUnderline.underline,
            lineThrough: charLineThrough.lineThrough,
            background: charBackground.background,
            underline: charUnderline.underline,
            underlineColor: charUnderlineColor.color,
            underlineStyle: charUnderlineStyle.style
        };
    }



        function highlightSelection(highlightColor) {
            highlightColor = 'rgba(173, 216, 230, 0.5)'; // 浅蓝色半透明
            let globalPos = 0; // 全局位置指针
           
            let lineSpacing=15;
            ctx.fillStyle = highlightColor; // 设置高亮颜色

            linesData.forEach(line => {
                const lineLength = line.text.length;
                const lineSelectionStart = Math.max(selectionStart - globalPos, 0);
                const lineSelectionEnd = Math.min(selectionEnd - globalPos, lineLength);

                if (lineSelectionStart < lineSelectionEnd) { // 有选中文本的行
                    const lineMetrics = calculateLineMetrics(ctx, line, lineSelectionStart, lineSelectionEnd);

                    // 直接使用line.y进行定位，加上偏移量
                    const startY = line.y  ;//- lineMetrics.maxHeight / 2; // 垂直居中调整

                    // 绘制高亮区域，应用X方向偏移和调整后的Y方向偏移
                    ctx.fillRect(lineMetrics.startX , startY, lineMetrics.totalWidth, lineMetrics.maxHeight);
                }

                globalPos += lineLength;
            });
        }

        function calculateLineMetrics(ctx, line, lineSelectionStart, lineSelectionEnd) {
            let maxHeight = 0; // 选区内最大字体高度
            let startX = Number.MAX_VALUE; // 选区开始的X坐标
            let totalWidth = 0; // 累计宽度，用于计算宽度
            let accumulatedWidth = 0; // 累计宽度，用于计算startX

            line.styles.forEach(style => {
                ctx.font = `${style.fontSize}px ${style.font}`; // 更新字体样式以进行测量
                const textPart = line.text.substring(Math.max(style.start, lineSelectionStart), Math.min(style.end, lineSelectionEnd));

                if (textPart.length > 0) {
                    const metrics = ctx.measureText(textPart);
                    const textWidth = metrics.width;
                    const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;

                    maxHeight = Math.max(maxHeight, textHeight); // 更新最大高度

                    // 确定选区开始的X坐标
                    if (style.start < lineSelectionStart) {
                        accumulatedWidth += ctx.measureText(line.text.substring(style.start, lineSelectionStart)).width;
                    } else if (startX === Number.MAX_VALUE) {
                        startX = accumulatedWidth; // 首次遇到重叠部分设置起始X坐标
                    }

                    totalWidth += textWidth; // 累加宽度
                    accumulatedWidth += textWidth;
                } else {
                    // 对于未选中的部分，累加宽度以确定后续选中部分的位置
                    accumulatedWidth += ctx.measureText(line.text.substring(style.start, style.end)).width;
                }
            });

            if (startX === Number.MAX_VALUE) {
                startX = 0; // 如果没有选中的文本，重置起始X坐标
            }

            return { startX, maxHeight, totalWidth };
        }

        function calculateTextWidth(text, fontSize, font) {
            // 实现具体的文本宽度计算逻辑
            // 可以使用canvas的measureText方法，这里需要传入ctx和正确设置font
            ctx.font = `${fontSize}px ${font}`;
            return ctx.measureText(text).width;
        }


        function drawCursor(position) {
            if (position === null || position < 0) return;

            let accumulatedHeight = 0; // 用于计算光标的Y坐标
            let cursorX = 0; // 光标的X坐标
            let cursorY = 0; // 光标的起始Y坐标
            let accumulatedLength = 0; // 累计字符长度，帮助定位光标所在行
            let lineFound = false; // 标记是否找到了光标所在行

            for (let line of linesData) {
                const lineHeight = line.height;
                if (position < accumulatedLength + line.text.length + 1 && !lineFound) {
                    // 光标位于当前行
                    cursorY = accumulatedHeight;
                    let linePosition = position - accumulatedLength; // 光标在当前行的位置
                    cursorX = linePosition === 0 ? 0 : getCursorXInLine(line, linePosition - 1);
                    lineFound = true; // 标记找到光标所在行，避免进一步循环
                }
                accumulatedLength += line.text.length;
                if (!lineFound) {
                    accumulatedHeight += lineHeight;
                }
            }

            // 绘制光标
            if (lineFound) {
                ctx.beginPath();
                ctx.moveTo(cursorX, cursorY);
                ctx.lineTo(cursorX, cursorY + lineHeight); // 使用当前行的高度作为光标高度
                ctx.strokeStyle = 'black';
                ctx.lineWidth = 1; // 可根据需要调整光标宽度
                ctx.stroke();
            }
        }


        function getCursorXInLine(line, position) {
            // 计算光标在行内的X坐标
            let cursorX = 0;
            for (let i = 0; i <= position; i++) {
                if (i < line.text.length) {
                    const char = line.text[i];
                    const charStyle = line.styles.find(s => i >= s.start && i < s.end) || { fontSize: 16, font: "Arial", color: "black" };
                    ctx.font = `${charStyle.fontSize}px ${charStyle.font}`;
                    const metrics = ctx.measureText(char);
                    if (i === position) return cursorX + metrics.width; // 如果到达光标位置，返回当前累计宽度
                    cursorX += metrics.width;
                }
            }
            return cursorX;
        }

        function clearSelection() {
                // 重置选择状态
                selectionStart = null;
                selectionEnd = null;

    			//isSelecting = false;
                // 如果需要在取消选择时也隐藏光标，取消以下注释
                // cursorPosition = null;
                // cursorVisible = false; // 可选：同时隐藏光标

                // 重新渲染画布以更新显示状态
                renderText();
            }

        
        function blinkCursor() {
                cursorVisible = !cursorVisible;
                renderText();
            }

           // setInterval(blinkCursor, 500); // 设置光标闪烁的间隔

            // 事件处理函数与之前相同，但需要更新以管理光标位置和可见性


        function getCharIndex(x) {
            for (let i = 0; i < charWidths.length; i++) {
                const { width, x: charX } = charWidths[i];
                if (x >= charX && x <= charX + width) {
                    return i;
                }
            }
            return null;
        }
        //用坐标返回当前字符内容
        function getCharByIndex(position) {
            // 累计前面所有行的字符长度，以定位到正确的字符
            console.log("getCharByIndx:",position);
            let accumulatedLength = 0;
            for (let line of linesData) {
                if (position < accumulatedLength + line.text.length) {
                    return line.text[position - accumulatedLength];
                }
                accumulatedLength += line.text.length;
            }
            return ''; // 如果索引超出范围，返回空字符串
        }


        function updateCursorPosition(x) {
            const rect = canvas.getBoundingClientRect();
            const clickX = x - rect.left; // 获取相对于canvas的点击位置
            cursorPosition = getCharIndex(clickX);
            cursorVisible = true; // 显示光标
        }

        function updateStylesForInsert(insertPosition, insertLength) {
            var isFind=false;
            console.log("C3 -> 更新样式！：",insertPosition);
            const selectedText = textData.text.substring(selectionStart, selectionEnd);
                    console.log("C3 选中文字",selectedText); // Print the selected text to the console

            var tempIndex=0;
                textData.styles.forEach(style => {

                    console.log("C1 -> forEach 更新样式！：",insertPosition," tempIndex:" ,tempIndex);
                    tempIndex++;
                    if(isFind==false)
                    {
                        // 如果插入位置在当前样式范围之前，或恰好在其开始位置
                        if (insertPosition==0) 
                        {
                           
                        	style.start =0;
                        	style.end += insertLength;
                            isFind=true;
                            console.log("C1 ->",insertPosition," 在当前样式之前  style.start="+insertPosition);
                            //当字符输入完成之后insertPosition会自增 ，要注意这个现象 。。
                        }
                        else
                        {  //插入位置在当前样式范围之间结束位置+1
                            if (insertPosition> style.start&&insertPosition <style.end)  
                            {
                                console.log("C1 -> ",insertPosition," 在当前样式之间");
                                style.end += insertLength;
                                isFind=true;
                            }else
                            {
                                if(insertPosition===style.end)
                                {
                                    console.log("C1 ->",insertPosition," 在当前样式最后尾");
                                    style.end += insertLength;
                                    isFind=true;
                                }

                            }

                           
                        }
                      
                        

                    }                    
                     else {
                        // 如果插入位置在当前样式范围内部，只需要调整结束位置
                        style.start += insertLength;
                        style.end += insertLength;
                    }
                    
                });
            }

    function renderSelection() {
    // 遍历每个字符，只有当字符索引在selectionStart和selectionEnd之间时才渲染高亮背景
        for (let i = selectionStart; i < selectionEnd; i++) {
            // 假设 getCharPosition 返回字符的位置和宽度
            let { x, y, width } = getCharPosition(i);
            // 绘制选区高亮背景
            ctx.fillStyle = 'rgba(173, 216, 230, 0.5)'; // 高亮颜色，可自定义
            ctx.fillRect(x, y - charHeight, width, charHeight); // 假设 charHeight 是字符的高度
        }
    }

    canvas.addEventListener('mousedown', function(e) {
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;

        if (e.button === 2) { // 检查是否为右键点击
            // 如果是在选区内点击右键，保持选区状态
            if (isInSelection(x, y)) {
                // 可以在这里触发其他与右键相关的行为，例如显示上下文菜单
                return; // 不清除选区也不开始新的选择
            }
            // 对于右键点击，如果不在选区内，也可以选择不处理
            return;
        }


        const charIndex = getCharIndex(x, y); // 通过 x, y 坐标获取字符索引
        if (charIndex !== -1) {
            isDragging = true;
            dragStartIndex = charIndex;
            selectionStart = charIndex;
            selectionEnd = charIndex + 1; // 开始时选中一个字符
            cursorPosition = getCharPosition(x, y); // 更新光标位置
        }
        renderText(); // 重新渲染文本以更新画布状态
    });


            function getCharIndex(x, y) {
                let accumulatedHeight = 50; // 用于累加行高，以确定点击位置的y坐标
 				let  lineSpacing=15;
                // 遍历每一行文本
                for (let lineIndex = 0; lineIndex < linesData.length; lineIndex++) {
                    const line = linesData[lineIndex]; //取出一行数据，，同时计算生成Y 坐标用于比对
                   
                    //console.log("C3 lineStr: " + line.text);
                    if (y >= accumulatedHeight && y < accumulatedHeight + line.height) {
                        // 确定鼠标点击在当前行内
                        let cursorX = 0; // 累加当前行字符的宽度，以确定点击位置的x坐标
                        let accumulatedLength = linesData.slice(0, lineIndex).reduce((acc, cur) => acc + cur.text.length, 0); // 累加之前所有行的字符长度，以确定全局字符索引

                        for (let i = 0; i < line.text.length; i++) {
                            const char = line.text[i];
                            // 查找该字符的样式
                            //console.log("C3 按字符查样：： ",char);
                            const charStyle = line.styles.find(s => i >= s.start && i < s.end) || { fontSize: 16, font: "Arial", color: "black" };
                            ctx.font = `${charStyle.fontSize}px ${charStyle.font}`;
                            const metrics = ctx.measureText(char);
                            const charWidth = metrics.width;

                            if (x >= cursorX && x < cursorX + charWidth) {
                                // 如果点击位置在当前字符的宽度范围内，则返回这个字符的全局索引
                                return accumulatedLength + i;
                            }

                            cursorX += charWidth; // 更新累加的宽度，以反映当前行内的位置
                        }
                        // 如果x坐标超出了当前行的最后一个字符，但y坐标仍在当前行内，则视为点击了该行的最后一个字符
                        return accumulatedLength + line.text.length - 1;
                    }
                    accumulatedHeight += line.height+lineSpacing; // 更新累加的行高，以反映不同行的位置
                }

                return -1; // 如果没有找到对应的字符，返回-1
            }

            //重原来返回下标索引 改为X,Y坐标

            function getCharPosition(x, y) {
                let accumulatedHeight = 50; // 用于累加行高，以确定点击位置的y坐标

                for (let lineIndex = 0; lineIndex < linesData.length; lineIndex++) {
                    const line = linesData[lineIndex];
                    //console.log("C3 lineStr: " + line.text);
                    if (y >= accumulatedHeight && y < accumulatedHeight + line.height) {
                        // 确定鼠标点击在当前行内
                        let cursorX = 0; // 累加当前行字符的宽度，以确定点击位置的x坐标
                        let accumulatedLength = linesData.slice(0, lineIndex).reduce((acc, cur) => acc + cur.text.length, 0); // 累加之前所有行的字符长度，以确定全局字符索引

                        for (let i = 0; i < line.text.length; i++) {
                            const char = line.text[i];
                            // 查找该字符的样式
                           // console.log("C3 按字符查样：： ",char);
                            const charStyle = line.styles.find(s => i >= s.start && i < s.end) || { fontSize: 16, font: "Arial", color: "black" };
                            ctx.font = `${charStyle.fontSize}px ${charStyle.font}`;
                            const metrics = ctx.measureText(char);
                            const charWidth = metrics.width;

                            if (x >= cursorX && x < cursorX + charWidth) {
                                // 如果点击位置在当前字符的宽度范围内，则返回这个字符的全局索引
                                return cursorX;
                            }

                            cursorX += charWidth; // 更新累加的宽度，以反映当前行内的位置
                        }
                        // 如果x坐标超出了当前行的最后一个字符，但y坐标仍在当前行内，则视为点击了该行的最后一个字符
                        return cursorX;
                    }
                    accumulatedHeight += line.height; // 更新累加的行高，以反映不同行的位置
                }

                return -1; // 如果没有找到对应的字符，返回-1
            }




            let lastSelectionEnd = null; // 全局变量，记录上次选择结束的位置

        //     canvas.addEventListener('mousemove', function(e) {
        //     if (isSelecting) {
        //         const rect = canvas.getBoundingClientRect();
        //         const x = e.clientX - rect.left;
        //         const y = e.clientY - rect.top;
        //         const charIndex = getCharIndex(x, y);

        //         if (charIndex !== -1) {
        //             if (selectionStart === null) {
        //                 // 初始化选择起点
        //                 selectionStart = charIndex;
        //             }
        //             // 更新选择结束位置
        //             selectionEnd = charIndex + 1;

        //             // 为了增强体验，使得选择区域能够逐字覆盖，我们需要确保光标位置精确
        //             // 这可能需要调整`getCharIndex`函数，使其更加精确地返回字符索引

        //             // 如果当前的选择终点与上一次不同，则需要重绘
        //             if (lastSelectionEnd !== selectionEnd) {
        //                 lastSelectionEnd = selectionEnd; // 更新记录的选择终点

        //                 console.log("C4 lastSelectionEnd", lastSelectionEnd);
        //                 renderText(); // 重新渲染文本以更新选择区域的高亮显示
        //             }
        //         }
        //     }
        // });


        canvas.addEventListener('mousemove', function(e) {
            if (!isDragging) return; // 如果没有在拖拽，就不执行任何操作

            const rect = canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            const charIndex = getCharIndex(x, y);

            if (charIndex !== -1) {
                if (charIndex < dragStartIndex) {
                    selectionStart = charIndex;
                    selectionEnd = dragStartIndex + 1; // 从右向左选择
                } else {
                    selectionStart = dragStartIndex;
                    selectionEnd = charIndex + 1; // 从左向右选择
                }
                renderText(); // 实时更新文本的渲染以反映选区的变化
            }
        });


        canvas.addEventListener('mouseup', function(e) {
            const rect = canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;

            if (e.button === 0) { // 确保是左键引起的mouseup事件
                isDragging = false; // 结束拖拽（选择）
             }
            
            if (isSelecting) {
                // 更新选择结束位置
                const charIndex = getCharIndex(x, y);

                if (charIndex >= 0) { // 确保得到了有效的字符索引
                    if (selectionStart !== null) {
                        selectionEnd = charIndex + 1;
                    } else {
                        selectionStart = charIndex;
                        selectionEnd = charIndex + 1;
                    }
                }

                if (selectionStart !== null && selectionEnd !== null && selectionStart !== selectionEnd) {
                    // 完成了有效选择
                    cursorVisible = false; // 根据需要隐藏光标
                } else {
                    // 未进行有效选择，可能仅是单击了位置
                    if (charIndex >= 0) {
                        updateCursorPosition(x); // 可能需要更新光标位置函数以适应多行
                    }
                    clearSelection(); // 清除选择
                }
            }

            isSelecting = false; // 重置选择状态
            renderText(); // 重新渲染以应用最终状态
        });


        canvas.addEventListener('dblclick', function(e) {
            const rect = canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;

            // 确认点击位置是否在已有的选区内
            if (isInSelection(x, y)) {
                // 如果是在选区内双击，我们需要累计选区内的文本
                let selectedText = "";
                let accumulatedLength = 0; // 累计长度以确定字符的全局索引位置

                for (let line of linesData) {
                    // 对于每一行，检查其是否部分或完全在选区内
                    if (accumulatedLength + line.text.length >= selectionStart && accumulatedLength <= selectionEnd) {
                        // 计算行内选区的开始和结束位置
                        const lineSelectionStart = Math.max(selectionStart - accumulatedLength, 0);
                        const lineSelectionEnd = Math.min(selectionEnd - accumulatedLength, line.text.length);
                        // 累计选区内的文本
                        selectedText += line.text.substring(lineSelectionStart, lineSelectionEnd);
                    }
                    accumulatedLength += line.text.length;
                    // 如果累计长度超过了选区的结束，就不需要继续检查后续的行了
                    if (accumulatedLength > selectionEnd) {
                        break;
                    }
                }

                console.log("C4 我双击了 ：：",selectedText); // 打印选中的文本到控制台
            }
        });


            function updateStylesForDelete(position, length) {
                console.log("updateStylesForDelete");
                textData.styles.forEach(style => {
                    if (position < style.start) {
                        style.start -= length;
                        style.end -= length;
                    } else if (position < style.end) {
                        style.end -= length;
                    }

                    // 防止start和end反转
                    if (style.start > style.end) {
                        style.start = style.end;
                    }
                });
            }


    document.addEventListener('keydown', function(e) {
                
                
                  // 检测是否同时按下了Ctrl键和C键
                  if (e.ctrlKey && e.key === 'c') 
                {
                    // 调用复制到剪贴板的函数
                    copyToClipboard();

                    e.preventDefault(); // 防止默认的复制行为
                }
             

                // 确保不是控制键，比如箭头键、Backspace等
                if (e.key.length === 1) {
                    // 在这里插入字符到textData.text
                    const beforeCursor = textData.text.slice(0, cursorPosition);
                    const afterCursor = textData.text.slice(cursorPosition);
                    console.log("英文输入 前：：",textData.text);

                    
                
                    textData.text = beforeCursor + e.key + afterCursor;
                    //在这里要调整输入字母的样式 ，增加了一个

                    console.log("英文输入后 ：：",textData.text," 插入位置  ",cursorPosition);
                    
                    updateStylesForInsert(cursorPosition,1);
                    electionStart = null;
                    selectionEnd = null;
                    // 如果需要在取消选择时也隐藏光标，取消以下注释
                    // cursorPosition = null;
                       cursorVisible = true; // 可选：同时隐藏光标
                        // 更新光标位置
                    cursorPosition++;

                    renderText(); // 重新渲染文本
                }

                if (e.key === 'Backspace' && cursorPosition > 0) {
                    const beforeCursor = textData.text.slice(0, cursorPosition - 1);
                    const afterCursor = textData.text.slice(cursorPosition);
                    textData.text = beforeCursor + afterCursor;
                    electionStart = null;
                    selectionEnd = null;

                    cursorPosition--;

                    renderText();
                } 
                else if (e.key === 'Delete' && cursorPosition < textData.text.length) {
                    const beforeCursor = textData.text.slice(0, cursorPosition);
                    const afterCursor = textData.text.slice(cursorPosition + 1);
                    textData.text = beforeCursor + afterCursor;

                    // 光标位置不变
                    electionStart = null;
                    selectionEnd = null;

                    renderText();
                }

                if (e.key === 'ArrowLeft' && cursorPosition > 0) {
                    cursorPosition--;
                    console.log("《《光标位置移动",cursorPosition);
                    electionStart = null;
                    selectionEnd = null;
                    
                    renderText();
                } 
                else if (e.key === 'ArrowRight' && cursorPosition <= textData.text.length) {
                    cursorPosition++;
                    console.log("光标位置移动=》》",cursorPosition);
                    var chartCount=0;

                    charWidths.forEach(objectOfChar => {
                        console.log('width: '+objectOfChar.width + ' x:' + objectOfChar.x);
                      
                        chartCount++;
                        

                    });

                    if(cursorPosition>chartCount)
                    cursorPosition=chartCount;
                    electionStart = null;
                    selectionEnd = null;

                    renderText();
                }

               

        renderText();
    });
    renderText();

});

    </script>
</body>
</html>
